#pragma once
#include <map>
#include <list>
class CDXUTDialogResourceManager;
class CDXUTDialog;
class CDXUTControl;
class CDXUTButton;
class CDXUTStatic;
class CDXUTCheckBox;
class CDXUTRadioButton;
class CDXUTComboBox;
class CDXUTSlider;
class CDXUTEditBox;
class CDXUTListBox;
class CDXUTScrollBar;
class CDXUTElement;
struct DXUTElementHolder;
struct DXUTTextureNode;
struct DXUTFontNode;

class Window
{
public:
	class FunctionSlotBase
	{
	public:
		virtual ~FunctionSlotBase(){}
		virtual bool operator()(CDXUTControl*) = 0;
	};

	template<typename T>
	class FunctionSlot : public FunctionSlotBase
	{
	public:
		typedef bool(T::*FunctionType)(CDXUTControl*);

		FunctionSlot(FunctionType func, T* obj) : mFunction(func), mObject(obj) {}
		virtual bool operator()(CDXUTControl* sender)
		{
			return (mObject->*mFunction)(sender);
		}

	private:
		FunctionType mFunction;
		T* mObject;
	};

	typedef std::list<Window*> WindowList;

public:
	Window();
	virtual ~Window();
	
	virtual void Close() = 0;
	virtual bool Open();
	void Destroy();

	static void CloseAll();

	CDXUTDialog* getDialog() { return mDialog; }
	const CDXUTDialog* getDialog() const { return mDialog; }

	bool FireEvent(int id, UINT event, CDXUTControl* sender = NULL);

	template<typename T>
	void Subscribe(int id, UINT event, bool(T::*callBack)(CDXUTControl*), T* obj);

	static int UniqueID()
	{
		static int IDBase = 0;
		return ++IDBase;
	}

protected:
	CDXUTDialog* mDialog;

	typedef std::multimap<UINT, FunctionSlotBase*> EventSlots;
	typedef std::multimap<int, EventSlots> ControlEvents;
	ControlEvents mControlEvents;

	static WindowList msWindowList;

protected:
	virtual bool OnOpen() { return true; }
	virtual void OnDestory() {}
};


template<typename T>
inline void Window::Subscribe(int id, UINT event, bool(T::*callBack)(CDXUTControl*), T* obj)
{
	ControlEvents::iterator it = mControlEvents.find(id);
	if (it == mControlEvents.end())
		it = mControlEvents.insert(ControlEvents::value_type(id, EventSlots()));

	it->second.insert(EventSlots::value_type(event, new FunctionSlot<T>(callBack, obj)));
}
